package com.example.netty.nio.channel;

import com.example.netty.nio.buffer.Buffers;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Set;

public class ServiceSocketDemo {
    public static class TCPEchoServer implements Runnable {
        //设置ip地址 InetSocketAddress  封装 ip的类
        private InetSocketAddress localAddress;

        public TCPEchoServer(int port) throws IOException {
            this.localAddress = new InetSocketAddress(port);
        }

        @Override
        public void run() {
            Charset utf8 = Charset.forName("utf-8");

            ServerSocketChannel ssc = null;  //服务通道
            Selector selector = null;
            try {
                /*创建选择器   */
                selector = Selector.open();

                ssc = ServerSocketChannel.open();
                ssc.configureBlocking(false); //设置非阻塞
                ssc.bind(localAddress, 1000); //绑定 ip 和端口   设置客户端最大缓存连接数z/后端排队 backlog  100
                ssc.register(selector, SelectionKey.OP_ACCEPT); //通道注册到选择器上 OP_ACCEPT --tcp
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println("Server start failed");
            }
            System.out.println("Server start with address :" + localAddress);
            /***
             * 服务器线程中断后退出
             */
            try {
                while (!Thread.currentThread().isInterrupted()) {

                    int n = selector.select(); //获取选择器 内容
                    if (n == 0) {
                        continue;

                    }
                    Set<SelectionKey> selectionKeys = selector.selectedKeys(); //通过key 找到对应事件
                    Iterator<SelectionKey> it = selectionKeys.iterator();
                    SelectionKey skey = null;
                    while (it.hasNext()) {
                        skey = it.next();
                        // 将已处理的事件进行删除  防止 下次select()方法返回已处理事件
                        it.remove();

                        try {
                            // 判断事件是否可接收  ssc通道 只能对链接事件感兴趣
                            if (skey.isAcceptable()) {
                                /*
                                 accept 方法 会返回一个普通通道
                                    每个通道在内核中都对应一个socket缓冲区
                                 */
                                SocketChannel sc = ssc.accept();
                                sc.configureBlocking(false);//设置非阻塞

                                int opRead = SelectionKey.OP_READ;
                                sc.register(selector, opRead, new Buffers(256, 256));
                                System.out.println("accept  from " + sc.getRemoteAddress());
                            }
                            //普通通道 感兴趣读事件 且有数据可读
                            if (skey.isReadable()) {
                                // 通过selectionKey 获得对应缓冲区
                                Buffers buffers = (Buffers) skey.attachment();
                                ByteBuffer readBuffer = buffers.getReadBuffer();
                                ByteBuffer writeBuffer = buffers.getWriteBuffer();
                                // 通过selectionKey 获得对应通道
                                SocketChannel sc = (SocketChannel) skey.channel();
                                //从底层的 socket读缓冲区中读入数据
                                sc.read(readBuffer);
                                readBuffer.flip();
                                //解码 显示  ， 客户端发送的消息
                                CharBuffer cb = utf8.decode(readBuffer);
                                System.out.println(cb.array());
                                readBuffer.rewind();
                                /*
                                准备好 向客户段发送的消息
                                //先写入 eche from service ，在写入收到消息
                                 */

                                writeBuffer.put("eche from service：".getBytes("utf-8"));
                                writeBuffer.put(readBuffer);
                                readBuffer.clear();
//                              //  设置通道写事件
                                skey.interestOps(skey.interestOps() | SelectionKey.OP_WRITE);

                            }

                            if (skey.isWritable()) {
                                Buffers buffers = (Buffers) skey.attachment();
                                ByteBuffer writeBuffer = buffers.getWriteBuffer();
                                writeBuffer.flip();
                                // 通过selectionKey 获得对应通道
                                SocketChannel sc = (SocketChannel) skey.channel();
                                //从底层的 socket读缓冲区中读入数据

                                int len = 0;

                                while (writeBuffer.hasRemaining()) {
                                    len = sc.write(writeBuffer);
                                    if (len == 0)
                                        break;
                                }
                                    writeBuffer.compact();

                                if (len != 0){
                                    skey.interestOps(skey.interestOps()& (SelectionKey.OP_WRITE));
                                }
                            }

                        } catch (IOException  e) {
                            System.out.println("service  encounter client error");
                            skey.cancel();
                            skey.channel().close();
                            e.printStackTrace();

                        }

                    }
                }


            } catch (IOException e) {

            }
        }
    }

}
